package cn.xiaoke.damowang_swagger.controller;

import cn.hutool.http.HttpUtil;
import cn.xiaoke.damowang_swagger.po.MdTemplateInfo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;

@RequestMapping("swaggerApi")
@Controller
public class SwaggerApiJSONController {

    @GetMapping("acquireApiString")
    public void acquireApiString(String url) throws IOException {


        final List<MdTemplateInfo> mdTemplateInfos = new ArrayList<>();


        final String s = HttpUtil.get(url);
        System.out.println("==================");
        System.out.println(s);
        System.out.println("==================");
        final JSONObject swagger = JSON.parseObject(s);
        final JSONObject info = swagger.getJSONObject("info");
        final JSONObject securityDefinitions = swagger.getJSONObject("securityDefinitions");
        final JSONObject definitions = swagger.getJSONObject("definitions");
        final JSONObject paths = swagger.getJSONObject("paths");
        final JSONArray tags = swagger.getJSONArray("tags");
        final String basePath = swagger.getString("basePath");
        System.out.println(swagger);
        System.out.println("=====================================");

        final Map<String, JSONObject> moduleMap = new HashMap<>();
        for (Map.Entry<String, Object> defEntry : definitions.entrySet()) {
            final String moduleName = defEntry.getKey();
            final JSONObject value = (JSONObject) defEntry.getValue();
            moduleMap.put(moduleName,value);
        }
        System.out.println(moduleMap);
        System.out.println("=====================================");
        final Map<String, String> controllerMap = new HashMap<>();
        for (int i = 0; i < tags.size(); i++) {
            final JSONObject tag = tags.getJSONObject(i);
            final String controllerName = tag.getString("name");
            final String controllerDesc = tag.getString("description");
            controllerMap.put(controllerName, controllerDesc);
        }
        for (Map.Entry<String, Object> entry : paths.entrySet()) {
            final String path = entry.getKey();
            final JSONObject value = (JSONObject) entry.getValue();
            value.entrySet().stream().findFirst().ifPresent(funcEntry -> {
                final String method = funcEntry.getKey();
                final JSONObject func = (JSONObject) funcEntry.getValue();
                final String functionName = func.getString("summary");
                final String functionDesc = func.getString("description");
                final String tag = func.getJSONArray("tags").getString(0);
                final String contentType = Optional.ofNullable(func.getJSONArray("consumes")).map( arr -> arr.getString(0))
                        .orElse(null);

                final MdTemplateInfo templateInfo = new MdTemplateInfo();

                templateInfo.setControllerTitle(tag)
                        .setControllerDesc(controllerMap.get(tag))
                        .setFunctionName(functionName)
                        .setFunctionDesc(functionDesc)
                        .setPath(basePath + path)
                        .setMethod(method)
                        .setContentType(contentType);
                final List<MdTemplateInfo.MdTemplateParamBlock> mdTemplateParamBlocks = new ArrayList<>();
                templateInfo.setParamBlocks(mdTemplateParamBlocks);
                final MdTemplateInfo.MdTemplateParamBlock block = new MdTemplateInfo.MdTemplateParamBlock();
                block.setTitle(null);
                mdTemplateParamBlocks.add(block);

                final List<MdTemplateInfo.MdTemplateParam> mdTemplateParams = new ArrayList<>();
                block.setParams(mdTemplateParams);

                for (Object parameter : func.getJSONArray("parameters")) {

                    final MdTemplateInfo.MdTemplateParam paramInfo = new MdTemplateInfo.MdTemplateParam();
                    mdTemplateParams.add(paramInfo);

                    final JSONObject param = (JSONObject) parameter;
                    final String enName = param.getString("name");
                    final String paramType = param.getString("in");
                    final String required = Optional.ofNullable(param.getString("required")).map(requi ->{
                        return requi.isEmpty() ? "非必输" : Boolean.parseBoolean(requi) ? "比输" : "非必输";
                    }).orElse("非必输");
                    final String cnName = param.getString("description");

                    paramInfo.setEnName(enName).setCnName(cnName).setParamType(paramType).setRequired(required);

                    final String dataType = Optional.ofNullable(param.getString("type")).orElseGet(()->{
                        final JSONObject schema = param.getJSONObject("schema");
                        String ref = schema.getString("$ref");
                        final HashSet<String> set = new HashSet<>();
                        set.add(ref);
                        recursionParamBlock(moduleMap,mdTemplateParamBlocks,ref,set);
                        final String type = schema.getString("type");

                        if (ref == null && type.equals("array")){
                            ref = schema.getJSONObject("items").getString("$ref");
                        }else{
                            ref = schema.getString("$ref");
                        }
                        return Optional.ofNullable(ref).map(r -> "**" +r.replace("#/definitions/","") + "**").orElse(null);
                    });
                    paramInfo.setDataType(dataType);
                }


                final JSONObject result = func.getJSONObject("responses").getJSONObject("200").getJSONObject("schema");
                if (result != null){
                    final String ref = result.getString("$ref");
                    final List<MdTemplateInfo.MdTemplateResultBlock> mdTemplateResultBlocks= new ArrayList<>();
                    templateInfo.setResultBlocks(mdTemplateResultBlocks);
                    final Set<String> set = new HashSet<>();
                    recursionResultBlock(moduleMap,mdTemplateResultBlocks,ref/*.replace("#/definitions/","")*/,set);
//                    if (!mdTemplateResultBlocks.isEmpty()) {
//                        mdTemplateResultBlocks.get(0).setTitle("响应");
//                    }
                }
                mdTemplateInfos.add(templateInfo);
            });
        }

        final Configuration configuration = new Configuration();
        configuration.setClassForTemplateLoading(SwaggerApiJSONController.class,"/ftl");
        final Template template = configuration.getTemplate("test01.ftl");
        final Map<String, MdTemplateInfo> freeMarkerMap = new HashMap<>();
        for (MdTemplateInfo mdTemplateInfo : mdTemplateInfos) {
            freeMarkerMap.put("info",mdTemplateInfo);
//            System.out.println(JSON.toJSONString(mdTemplateInfo, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue,
//                    SerializerFeature.WriteDateUseDateFormat));
//            System.out.println("=========================================================");
            try(final StringWriter writer = new StringWriter()){
                template.process(freeMarkerMap,writer);
                writer.flush();
                final String txt = writer.toString();
                if ("/damowang_swagger/sysUser/list/{pageNo}/{pageSize}".equals(mdTemplateInfo.getPath())){
//                if (mdTemplateInfo.getPath().equals("/damowang_swagger/sysUser/save")){
                    System.out.println(txt);
                }
                final String path = this.getClass().getResource("").getPath();
                System.out.println(path);
                File file = new File("D:\\javaproject\\bilibili\\damowang_swagger\\src\\main\\resources\\" + "doc");
                if (!file.exists()) {
                    file.mkdirs();
                    System.out.println("创建文件夹");
                }
                System.out.println(file.getPath());
                File controllerFile = new File(file.getPath() + "\\" + mdTemplateInfo.getControllerTitle());
                if (!controllerFile.exists()) {
                    controllerFile.mkdirs();
                    System.out.println("创建文件夹2");
                }
                File methodFile = new File(controllerFile.getPath() + "\\" + mdTemplateInfo.getFunctionName()+".md");
                try (FileWriter fileWriter = new FileWriter(methodFile)){
                    template.process(freeMarkerMap,fileWriter);
                }
            } catch (TemplateException e) {
                e.printStackTrace();
            }
        }

    }

    private void recursionParamBlock(Map<String, JSONObject> moduleMap, List<MdTemplateInfo.MdTemplateParamBlock> list, String ref,Set<String> set){
        if (ref == null || ref.isEmpty()) return;
        ref = ref.replace("#/definitions/","");
        if (set.contains(ref)) return;
        set.add(ref);
        final JSONObject param = moduleMap.get(ref);
        if (param == null){
            return;
        }
        final JSONObject properties = param.getJSONObject("properties");

        final MdTemplateInfo.MdTemplateParamBlock block = new MdTemplateInfo.MdTemplateParamBlock();
        final List<MdTemplateInfo.MdTemplateParam> params = new ArrayList<>();
        block.setParams(params).setTitle(ref);
        list.add(block);

        for (Map.Entry<String, Object> entry : properties.entrySet()) {

            final MdTemplateInfo.MdTemplateParam templateParam = new MdTemplateInfo.MdTemplateParam();

            final String enName = entry.getKey();
            final JSONObject value = (JSONObject) entry.getValue();
            String dataType = value.getString("type");
            String newRef;
            if (dataType != null && dataType.equals("array")){
                newRef = value.getJSONObject("items").getString("$ref");
                if (newRef != null){
                    dataType = "array[ **" + newRef.replace("#/definitions/","") +"** ]";
                }
            }else{
                newRef = value.getString("$ref");
            }
            final String cnName = value.getString("description");
            final String example = value.getString("example");


            templateParam.setEnName(enName).setCnName(cnName).setDataType(dataType).setExample(example);

            params.add(templateParam);

            recursionParamBlock(moduleMap,list,newRef,set);
        }

    }
    private void recursionResultBlock(Map<String, JSONObject> moduleMap, List<MdTemplateInfo.MdTemplateResultBlock> list, String ref,Set<String> set){
        if (ref == null || ref.isEmpty()) return;
        if (set.contains(ref)) return;
        set.add(ref);
        ref = ref.replace("#/definitions/","");
        final JSONObject param = moduleMap.get(ref);
        final JSONObject properties = param.getJSONObject("properties");
        if (properties == null) return;

        final MdTemplateInfo.MdTemplateResultBlock block = new MdTemplateInfo.MdTemplateResultBlock();
        final List<MdTemplateInfo.MdTemplateResult> params = new ArrayList<>();
        block.setParams(params).setTitle(ref);
        list.add(block);

        for (Map.Entry<String, Object> entry : properties.entrySet()) {

            final MdTemplateInfo.MdTemplateResult templateParam = new MdTemplateInfo.MdTemplateResult();

            final String enName = entry.getKey();
            final JSONObject value = (JSONObject) entry.getValue();
            String dataType = value.getString("type");
            String newRef;
            if (dataType != null && dataType.equals("array")){
                newRef = value.getJSONObject("items").getString("$ref");
                if (newRef != null){
                    dataType = "array[ **" + newRef.replace("#/definitions/","") +"** ]";
                }
            }else{
                newRef = value.getString("$ref");
                if (newRef != null && !newRef.isEmpty()){
                    dataType = "**"+newRef.replace("#/definitions/","")+"**";
                }
            }

            final String cnName = value.getString("description");
            final String example = value.getString("example");

            templateParam.setEnName(enName).setCnName(cnName).setDataType(dataType).setExample(example);

            params.add(templateParam);

            recursionResultBlock(moduleMap,list,newRef,set);
        }

    }

}
